from constants import GAN, ZHI, ANIMALS, KONG_WANG, GUA_SHU, HIDE_SHU, FULL_GUA_NAME, FAMILY, LIU_QIN_NUM_60
from function_tools.CustomClass import AttrDict
from handle_text.handle_time import time_str_process,GetCurrentTime
from handle_text.handle_gua_params import handle_coinNum

msg_dict = {'title': '', 'my': None, 'coinNum': [], 'gua': None, 'biangua': None}
time_dict = {'Y': None, 'Yg': None, 'm': None, 'd': None, 'dg': None, 'H': None}


# 不存在则初始化


def input_process():
    global time_dict
    global msg_dict

    title = input('标题：')
    time_str = input('时间：')
    coinNum = input('硬币数组：')
    msg_dict['title'] = title
    msg_dict.update(handle_coinNum(coinNum))  # TODO：
    time_dict = time_str_process(time_str)


def GuideComponent(title, coinNum, time_str):
    msg_dict['title'] = title
    msg_dict.update(handle_coinNum(coinNum))  # TODO：
    time_dict = time_str_process(time_str)
    return time_dict, msg_dict


class LiuyaoPlace:
    def __init__(self, time_dict: dict = None, msg_dict: dict = None):

        self.time = AttrDict()
        if isHaveMonthDate(time_dict):  # 通过 time_dict 指定排盘时辰(同时具备月、日、日干)
            self.time.update(time_dict)
        else:
            self.time.update(GetCurrentTime(hour=time_dict['H'], Mode=int))  # 还可以指定时辰,

        self.gua = AttrDict()
        self.gua.update(
            {'title': '', 'theme': '', 'coinNum': [int(i) for i in '102012'], 'gua': 25, 'biangua': 26, })
        self.gua.update(msg_dict)
        self.update_parameters()



    def place(self, output=False,isHtml=False,):
        cent = 14 if isHtml else 8  # 在火狐 16 比较合适，在edge或QQ浏览器 14 比较合适
        if 'msg' in self.time:
            text = f'占题：{self.gua.title}\n公历时间：{self.time.msg}\n当前时间：{get_time_info(self.time)}'
        else:
            text = f'占题：{self.gua.title}\n当前时间：{get_time_info(self.time)}'

        sign = ["×", '′', '″', "○"]
        main_lst, after_lst, hide_lst = [], [], []
        Family = palace_12(self.gua.wx)
        for rank, num in enumerate(self.gua.GuaShu_li):
            string = Family[num % 12] if num else ''
            if rank < 6:
                main_lst.append(string)
            elif rank < 12:
                after_lst.append(string)
            else:
                hide_lst.append(string)
        if any(hide_lst):
            hide_lst = [n if n else ' ' * cent for n in hide_lst]

        self_other = [n if n else ' ' * 2 for n in self_other_ps(self.gua.self, self.gua.other, skip=True)[:6]]
        six_animals = Sixmode_to_Animals(self.gua.SixMode)

        condition = self.gua.biangua != self.gua.gua and self.gua.biangua != 64
        headline = ' ' * 6 + FULL_GUA_NAME[self.gua.biangua] if condition else ''
        text += '\n' + FULL_GUA_NAME[self.gua.gua] + headline

        for i in range(6):
            n = self.gua.coinNum[i]
            lst = [six_animals[i], hide_lst[i], main_lst[i] + sign[n], self_other[i],
                   after_lst[i] + sign[(n + 3) // 3] if after_lst[i] else '']
            line = ' '.join(lst)
            text += '\n' + line

        if output:
            print(text)
        else:
            return text

    def coinNum_change(self, mode='reverse'):
        if mode == 'reverse':
            self.gua.coinNum = [self.gua.coinNum[5 - i] for i in range(6)]
        elif mode == 'change':
            self.gua.coinNum = [3 - n for n in self.gua.coinNum]

        print('already changed to :', ''.join([str(i) for i in self.gua['coinNum']]))

    def update_parameters(self):
        self.gua['wx'], self.gua['SixMode'] = h_calculate(self.gua.gua, self.time.dg)
        self.gua['trigger'] = [1 if r in [3, 0] else 0 for r in self.gua.coinNum]
        self.gua['self'], self.gua['other'] = search_self(self.gua.gua)  # 爻位1-6
        self.gua['GuaShu_li'] = gua_info_list_calculate(self.gua.gua, self.gua.biangua, self.gua.trigger)
        self.gua['yong_shen'] = '妻财'
        self.gua['DB_PATH'] = '妻财'
        self.gua['FILE_PATH'] = '妻财'

        self.cache = AttrDict()
        self.cache.update({'tmp': None, 'line_belong': None, 'former_idxlst': None,'suffix_set':[]})

        self.GuaImageDict = AttrDict()

    def check_out_articles(self, yong_shen, file_path):

        def decode(articles):
            result = ""
            # print(articles)
            for (saying, nested_lst) in articles:  # key:list[list[str]]
                string = '·'
                for condition in nested_lst:
                    sentence = ' '.join(condition).strip()  # 避免牵扯到换行符
                    string += sentence + '，'
                result += f"{string[:-1]}：\n{saying.strip()}\n"  # 截去最后的，| saying“结论”

            return result

        条文箩筐 = [[], [], [], [], [], [], []]  # idx range in (0~6)

        from handle_text.FileReader import file_handler
        for dic in file_handler(yong_shen, file_path):
            for saying, condition_dic in dic.items():
                self.cache.line_belong = None
                if self.identify_sentence(condition_dic):
                    # TODO：增强替换灵活性？在此处替换saying，【bug】puzzle:replace '[逢冲逢值]' by class's element   定位
                    if self.cache.suffix_set:
                        saying += '\n# ' + "\n# ".join(set(self.cache.suffix_set))
                        self.cache.suffix_set = []
                        condition_dic = [[n for n in lst if not n.startswith('%')] for lst in condition_dic]

                    if self.cache.line_belong is None:
                        self.cache.line_belong = 6
                    try:
                        条文箩筐[self.cache.line_belong].append((saying, condition_dic))
                    except Exception as e:
                        print(e, saying, condition_dic, self.cache.line_belong)
                    self.cache.line_belong = None
                    break

        result_lst = [f"line:{i + 1}\n{decode(articles_dic)}\n" for i, articles_dic in enumerate(条文箩筐) if
                      articles_dic]
        print(''.join(result_lst).strip())

    def identify_sentence(self, nested_lst: list[list[str]] = None) -> bool:
        """
        功能：正常检查token；有以“或”开头的，分组器工作，分组检查token
        :param nested_lst:  形如 nested_list = [
             ["A", "B", "C"],
             ["或D", "E"],
             ["或", "F", "G"],
             ["H", "I", "J"]
         ]
        :return:
        """

        def sentence_process(condition_section: list[str]) -> bool:  # condition_section：[obj,verb,verb,]

            def any_(idx):
                for item in todo:
                    if self.select_cell(idx, item):
                        return True
                else:
                    return False

            # 传入condition_section；返回idx列表，condition_section
            # 识别obj，筛选的verb。剔除筛选的verb。

            obj_idx_list, condition_section = self.identify_obj(condition_section)
            if not obj_idx_list:
                # 如果obj_idx_list为空/None，说明条件不成立，返回False。
                return False
            elif obj_idx_list is True or len(condition_section) < 2:
                # obj_idx_list is True，提前跳出函数，返回True或False。仅有两个词的【短句子】。
                # 如果 condition_section 长度小于2，下文无法做判断。既然 obj_idx_list 非空，
                return True

            for string in condition_section[1:]:
                if '或' in string:  # A或B或C：类型分割。
                    todo = string.split('或')
                    condition_lst = [any_(idx) for idx in obj_idx_list]
                    # 原先方案：A，B，C只要有一个元素为真，马上 break 打破循环。这样不好，无法筛选符合 string 条件的 idx

                elif string[0] in '%|*':
                    if string == '%':
                        print('★这句断语还在使用%，请你改掉：', ''.join(condition_section))
                        # self.save_idx_lst.extend(obj_idx_list)  # 这个语法，可以删除的，一律改用*
                    elif string.startswith('*'):
                        self.cache[string.replace('*', '上述')] = obj_idx_list
                    # else:
                    #     obj_idx_list += self.save_idx_lst
                    #     self.save_idx_lst = []
                    #     self.add_suggestion_suffix(string, obj_idx_list)  # 运算之后赋值类属性【已加工】
                    continue

                else:  # A，不带或字
                    # obj_idx_list多元素的情况：遍历每个元素，只要有一个True即可，any
                    # 原来的代码     condition = any(self.select_cell(idx, string) for idx in obj_idx_list)
                    self.cache.tmp = condition_section
                    condition_lst = [self.select_cell(idx, string) for idx in obj_idx_list]

                if not any(condition_lst):  # any 只要有一个对就好；not any 一个对的都没有；这个string没有过关，劝退；过关了，进入下个loop
                    return False
                elif string[0] == '皆' and not all(condition_lst):  # 皆 要求
                    return False

                if (leng := len(condition_lst)) >= 2 and string[0] != '+':
                    obj_idx_list = [obj_idx_list[i] for i in range(leng) if condition_lst[i]]
                    # 新改的代码，根据对错，调节obj_idx_list
                self.cache.former_idxlst = obj_idx_list

            # for 循环每一个单词都过关了，才能return True
            if not self.cache.line_belong:
                self.cache.line_belong = self.GuaImageDict["爻序"][obj_idx_list[0]] % 6 if obj_idx_list else None
            return True

        def 生成器分组(or_list, group_list):
            # “或”开头，到下一个“或”分为一组
            for i in range(len(or_list)):
                or_index = or_list[i]
                if i == 0:
                    yield group_list[:or_index]
                if i == len(or_list) - 1:
                    yield group_list[or_index:]
                else:
                    yield group_list[or_index:or_list[i + 1]]

        if any([li[0].startswith("或") for li in nested_lst]):
            或的索引列表 = [i for i, li in enumerate(nested_lst) if li[0].startswith("或")]
            # 如果存在以“或”开头的，触发分类
            genator = 生成器分组(或的索引列表, nested_lst)
            for group_list in genator:
                if all(sentence_process(li) for li in group_list):
                    return True
            self.cache.line_belong = None
            return False
        else:
            if all(sentence_process(li) for li in nested_lst):
                # 嵌套列表：[[obj_1,verb,verb,],[obj_2,verb,verb,],[obj,verb,verb,],...]     li：[obj,verb,verb,]
                return True
            else:
                self.cache.line_belong = None
                return False

    def identify_obj(self, condition_section) -> tuple[list[int], list[str]]:
        '''
        找到obj_lst，再根据上下文调整condition_section
        :param condition_section:
        :return:obj_lst,condition_section;[idx1,idx2,...],[obj,section1,section2,...]
        '''
        if len(condition_section) >= 3:
            obj, verb_1, verb_2 = condition_section[:3]
        else:
            obj, verb_1, verb_2 = condition_section + [None] * (3 - len(condition_section))  # 考虑下文

        if obj[0] in ['或', '·']:  # 只是一个识别的符号，截去，不能带“或”字开头；主语obj 带有”或“字，需要分割
            obj = obj[1:]

        if '或' in obj:
            obj_lst = []
            split_sub_objs = obj.split('或')
            IsSingleObj = all(i in SINGLE_OBJ for i in split_sub_objs)

            for sub_obj in split_sub_objs:
                result, condition_section = self.对象识别([sub_obj] + condition_section[1:])
                if IsSingleObj:  # 单主语成句
                    if result:
                        obj_lst = result
                        break
                    else:
                        obj_lst = result

                else:  # 主语序数，叠成列表
                    if isinstance(result, list):
                        obj_lst += result
                    else:
                        print('不希望的主语：', condition_section)
                        obj_lst = []

            return obj_lst, condition_section

        verb_li = [verb for verb in [verb_1, verb_2] if verb in self.lines_animals and verb not in '月建|日辰']  # 起码

        def filter_and_drop(IdxList, ConditionSection):
            # 过滤和筛选：verb_li非空时触发
            for verb in verb_li:
                ConditionSection = [i for i in ConditionSection if i != verb]
                try:
                    IdxList = [num for num in IdxList if self.GuaImageDict['爻序'][num] % 6 == self.lines_animals[verb]]
                except TypeError:
                    lst = [self.GuaImageDict['爻序'][num] for num in IdxList]
                    print('这一条写的有问题：', ConditionSection, '结果是：', lst)
            return IdxList, ConditionSection

        def check_former_idx(idx=None):
            if not idx:
                if not self.cache.former_idxlst:  # 与前文所提及的 主语obj 密切相关
                    return []
                idx = self.cache.former_idxlst[0]
            elif isinstance(idx, list):
                result = [check_former_idx(i) for i in idx]
                flattened_result = [item for sublist in result for item in sublist]
                return flattened_result
            # elif isinstance(idx,int)

            lst = self.GuaImageDict['爻序']
            idx_lst = []

            if obj == '入卦':  # 变爻（爻序）
                idx_lst = self.主卦六亲索引(self.GuaImageDict['六亲'][idx]) if self.isTargetYao(
                    '变爻', idx) else []
            elif obj.endswith('动爻'):
                idx_lst = [lst.index(lst[idx] - 6)] if self.isTargetYao('变爻', idx) else []
            elif obj == '化出':
                idx_lst = [lst.index(lst[idx] + 6)] if self.isTargetYao('动爻', idx) else [idx] if self.isTargetYao(
                    '变爻') else []
            elif obj == '飞神':  # TODO：仿照化出修改，否则容易找错飞神
                idx_lst = [lst[idx] % 6] if self.isTargetYao('伏神', idx) else []
            elif obj == '飞神化出':
                cond = self.isTargetYao('伏神') and self.isTargetYao('动爻', lst[idx] % 6)
                idx_lst = [lst.index(lst[idx] - 6)] if cond else []

            return idx_lst

        if obj in self.cache:
            idx_list = self.cache[obj]
            # current:take it out
            # puzzle:some place is up to memory, cannot use 'self.cache[obj] = idx_list' to add to 'self.cache[obj]',
            # solution:this case 'cannot add' can be the following 'elif' ,the case need to add in else (unsure be another loop)

        elif obj in ['墓库', '该爻']:
            idx_list = [self.Memory_idx] if self.Memory_idx else []
        elif obj in ['此动爻', '入卦', '化出', '飞神', '飞神化出']:  # 飞神会被cache截胡，代码在找伏神idx_lst的函数里
            idx_list = check_former_idx()
        elif obj[:2] == '上述':
            if obj in '上述入卦|上述动爻|上述化出|上述飞神|上述飞神化出' and '上述' in self.cache:
                obj = obj[2:]
                idx_list = check_former_idx(self.cache['上述'])
            else:
                idx_list = []

        elif obj in ['三合局', '半合局', '三刑']:
            idx_list = self.last_idx_group
        elif obj in SINGLE_OBJ and obj not in ['二爻', '三爻', '四爻', '独发', '世爻']:
            '''
            it is already a cache ,can directly access and not necessary to save once again!
            【说明】此类句子只有两个单词.
            example:六合；独发；主卦 谦；卦宫五行 土；外卦 化艮 / 巽化乾 / 巽
            '''
            if obj not in self.GuaInfo:  # 没有就补
                self.single_obj_identify(obj)

            if not verb_1:  # example:'六合 / 独发:True'
                return self.GuaInfo[obj], condition_section
            elif '或' in verb_1:  # A或B或C：类型分割。
                todo = verb_1.split('或')
                if any(item in self.GuaInfo[obj] for item in todo):  # A，B，C只要有一个元素为真，返回True。
                    return True, condition_section
                return False, condition_section
            else:
                try:
                    condition = verb_1 in self.GuaInfo[obj]
                    return condition, condition_section
                except TypeError:
                    print('为何 verb_1 is bool?', '-'.join(condition_section))
                    return True, condition_section
                except KeyError:
                    print('为何 verb_1 is bool?', '-'.join(condition_section))
                    return True, condition_section
        else:
            # 找到 并储存 主语obj 对应的 idx_list:[idx:int]
            # all the case of 'obj' in this 'else',write down in 'self.cache[obj]' as a fixed value in the end,so that
            # next time will not go to this 'else'
            if obj in LIU_QIN:
                idx_list = self.主卦六亲索引(obj)
                if verb_1 and verb_1 in ['持世', '临应', '六爻', '五爻', '四爻', '三爻', '二爻', '初爻']:
                    idx_list = [] if idx_list[0] >= 12 else idx_list  # 【bug】以前为了限制'伏神持世'

            elif obj in self.lines_animals:
                idx_list = [self.lines_animals[obj]]  # example: 二爻 生世；朱雀 发动
            elif obj in '子丑寅卯辰巳午未申酉戌亥|金木水火土':
                idx_list = self.idx_set(obj)

            elif obj in ['父母伏藏', '兄弟伏藏', '子孙伏藏', '妻财伏藏', '官鬼伏藏']:
                # using two chars find out 'idx_list',such as : [14],List[int].
                # string ,
                idx_list = self.伏藏索引(obj[:2])

            elif obj in ['父母化出', '兄弟化出', '子孙化出', '妻财化出', '官鬼化出']:
                idx_list = self.变爻索引(obj[:2])

            elif obj.endswith('化出') and obj[:2] in self.lines_animals:
                li = [self.lines_animals[obj[:2]]]
                idx_list = self.变爻索引(动爻索引=li)

            elif obj in ['父母两现', '兄弟两现', '子孙两现', '妻财两现', '官鬼两现']:
                # FindHide = verb_1!='持世'
                idx_list = self.主卦六亲索引(obj[:2])
                if len(idx_list) != 2:  # 不是两现，劝退
                    return False, condition_section
                if not verb_1:  # 没有下文【verb】就返回对错
                    return True, condition_section

            elif obj in SHEN_SHA_STR:
                zhi: str = self.search_shen_sha(obj)  # 没有年份查年神煞，这里会出来，None
                idx_list = self.idx_set(col='支', obj_list=zhi) if zhi else []

            elif obj in '月建|日辰':
                idx_dic = {'日辰': [-1], '月建': [-2]}
                idx_list = idx_dic[obj]

            elif obj in '独发|独发化出|所冲|所合|变爻所冲|变爻所合':
                self.add_idxlst('独发')
                idx_list = self.cache[obj]
            elif obj in '世爻|世爻化出|世爻所冲|世爻所合|世爻变爻所冲|世爻变爻所合':
                self.add_idxlst('世爻')
                idx_list = self.cache[obj]

            else:
                # the case can't identify,must return right now...
                print(f"碰到无法处理的情况。主语是：【{obj}】，完整断语：【{'-'.join(condition_section)}】。")
                return [], []

            self.cache[obj] = idx_list

        if '持世' in verb_li:  # 对‘伏神持世’优化
            idx_list = [n for n in idx_list if n < 6]

        if verb_li:
            idx_list, condition_section = filter_and_drop(idx_list, condition_section)

        # self.cache.former_idxlst = idx_list  # 记住前一个obj[new]
        return idx_list, condition_section

    def select_cell(self, idx: int, string: str, add_to_memory: bool = True, condition: bool = False) -> bool:
        """
        检查单元格：选择一个单元格，判断是不是
        实现一次对错判断。并且利用记忆化搜索，尽量保证不再判断第二次。
        :param idx: 形如GID词典={key:[item1,item2,...]}，指定了item的索引，也是表格的行索引
        :param string: 利用此变量，非常灵活的切换检查的行列。规则较为繁琐。
        :param add_to_memory:是否加入记忆，方便记忆化搜索？
        :return: True|False，完成一次对错判断

        分为两类，
        if-elif放置的是：
        GID表格里查询不到的数据，定condition需要外部函数辅助。为了方便可以考虑纳入GID表格。
        else内部放置的是：
        通过查询GID（表格数据==标准答案？dict[key][idx] == value？）实现判断
        """
        # if not isinstance(idx, int):
        #     print(idx, string, '★★★★ 此处有误，类型为', type(idx))
        if idx not in self.cache:  # 若“cache”没有 键idx的记录，则创建
            self.cache[idx] = {}
        elif string in self.cache[idx]:  # 若“cache”已有 键idx、string的记录，则利用
            condition = self.cache[idx][string]
            return condition

        if string[0] in '非|不|皆|+':
            if string[0] in '皆|+':
                string = string[1:]
            else:
                return not self.select_cell(idx, string[1:])

        if string in self.convert_string['外部函数']:
            dizhi, value, funtion = self.convert_string['外部函数'][string]

            self_dizhi = self.gua.GuaShu_li[dizhi] % 12
            active_dizhi = self.GuaImageDict['60六亲'][idx] % 12
            condition = funtion(self_dizhi, active_dizhi) == value

        elif string in self.convert_string['动爻列标志']:
            '''
            检查表格的一列或多列（col_range）。找到符合条件的列时，列号转为动爻的idx，加入记忆。
            例如：
            妻财伏藏 死地发动   pure_posi>=12
            官鬼化出 回头克；   pure_posi<12
            妻财持世 回头克    pure_posi<6     【注】pure_posi 两种范围 代表 主卦和伏藏，is valid_line_range
            '''
            col_range, value = self.convert_string['动爻列标志'][string]
            if self.isTargetYao('变爻', idx) or col_range == []:  # valid_line_range:主卦和伏藏;自检测：list:[key]非空
                self.cache[idx][string] = False
                return False
            for col in col_range:
                if condition := self.GuaImageDict[col][idx] == value:
                    self.Memory_idx = int(col) // 3  # col_range in special decoding way,动爻列(客) 转 idx(主)
                    # convert table_column(动变爻对应的列) to table_row(as well as idx) and save
                    break
            if string == '入动墓' and condition and self.Memory_idx not in self.cache['动墓']:
                self.vary_mort_IdxSet(idx)

        elif string in '阴阳':
            if self.isTargetYao('伏神', idx):
                return False
            string_map = {0: '阴', 1: '阳'}
            condition = string == string_map[self.coinsNumber_list[idx] % 2]

        elif string in ['三合局', '半合局', '外三合局']:
            condition = self.find_sanhe(idx, string)

        else:
            # 包括 所有 六亲、六神、地支、返卦、五行:
            if string in STATUS:
                key, value = self.search_kind(string), string
            elif string in self.convert_string['六亲六神']:
                key, value = self.convert_string['六亲六神'][string]

            # 包括 所有 日月 生克扶、冲合值、十二长生、刑、下一个四值；动爻 类型
            elif string in self.convert_string['日月发动世应']:
                key, value = self.convert_string['日月发动世应'][string]

            elif string in VARY_LINE:  # 动爻的 idx 不改动，换算为 键 key 的值。本函数的最后，使用 idx,key,string 校对。
                if idx >= 6 or self.trigger_li[idx] == 0:  # 条件:只有【 主卦】【 发动】的动爻，才有。否则在这里要劝退。
                    self.cache[idx][string] = False
                    return False

                # convert_string_alive = {   '变爻列标志' # using 'idx' to adjust col_name  '回头生': [str(18 + idx * 3), '生'], '回头克': [str(18 + idx * 3), '克'],  '化进': [str(18 + idx * 3), '进'], '化退': [str(18 + idx * 3), '退'], '化合': [str(19 + idx * 3), '合'], '反吟': [str(19 + idx * 3), '冲'], '伏吟': [str(19 + idx * 3), '值'],  '化死地': [str(20 + idx * 3), '死'],  # 为什么是18，19，20？主卦才有化死地，化墓，主卦的idx一定小于6'化墓': [str(20 + idx * 3), '墓'], '化绝': [str(20 + idx * 3), '绝']}   key, value = convert_string_alive[string]
                if string[-1] in '生|克|进|退':
                    key, value = str(18 + idx * 3), string[-1]
                elif string in '化合|反吟|伏吟':
                    dic = {'化合': '合', '反吟': '冲', '伏吟': '值'}
                    key, value = str(19 + idx * 3), dic[string]
                elif string[1:] in LIFE_AND_DEATH_12_FORTUNE:
                    key, value = str(20 + idx * 3), LIFE_AND_DEATH_12_FORTUNE[string[1:]]
                elif string[1:] in '墓|绝':
                    key, value = str(20 + idx * 3), string[1:]

            elif string[0] == '化':  # 强行改动idx，转到动爻对应的变爻idx。本函数的最后，使用 idx,key,string 校对。
                if idx >= 6 or self.trigger_li[idx] == 0:  # 条件:【主卦】【发动】的动爻 才有化空亡、化月破。
                    self.cache[idx][string] = False
                    return False
                if string in self.convert_string['变爻行标志']:
                    key, value = self.convert_string['变爻行标志'][string]
                elif string[1:] in STATUS:
                    key, value = self.search_kind(string[1:]), string[1:]
                elif string[1:] in self.convert_string['日月发动世应']:
                    # 变更 表格检测行的idx 变更为变爻行idx。加入记忆。不符合条件劝退
                    key, value = self.convert_string['日月发动世应'][string[1:]]
                elif string[1:] in SHEN_SHA_STR:
                    key, value = '支', self.search_shen_sha(string[1:])
                elif string[1:] in TIME_STRING:
                    key, value = self.time_string_identify(string[1:])
                else:
                    print('★starts with 化,but undefined string:', string)
                    return False

                idx = self.GuaImageDict['爻序'].index(idx + 6)  # 化墓 在子孙；化空亡 在子孙
                self.Memory_idx = idx
                add_to_memory = False  # 不加入记忆

            elif string.endswith('同宫'):  # 父母同宫？
                prefix = string[:-2]
                fua_gua = self.GuaImageDict['返卦'][idx]

                if prefix in self.lines_animals:
                    n = self.lines_animals[prefix]
                    if n == idx:  # 自己和自己同宫，没有意义
                        return False
                    key, value = '返卦', self.GuaImageDict['返卦'][n]
                elif string == '变爻同宫':
                    if not self.isTargetYao('动爻', idx):
                        return False
                    by_idx = self.GuaImageDict['爻序'].index(idx + 6)
                    key, value = '返卦', self.GuaImageDict['返卦'][by_idx]
                elif prefix in '父母|官鬼|妻财|子孙|兄弟':
                    idx_lst = [n for n in self.idx_set(fua_gua, '返卦') if n != idx]
                    condition = any(self.GuaImageDict['六亲'][i] == prefix for i in idx_lst)
                    return condition

            elif string in '外卦|内卦|世上|世下|应上|应下|伏藏':
                rank = self.GuaImageDict['爻序'][idx]
                value_dict = {
                    '外卦': rank if rank % 6 < 3 else False, '内卦': rank if 3 <= rank % 6 <= 5 else False,
                    '伏藏': rank if 18 > rank >= 12 else False,  # 以上对非主卦的idx也成立
                    '世上': self.self_posit - 1 if self.self_posit > 0 else False,
                    '世下': self.self_posit + 1 if self.self_posit < 5 else False,
                    '应上': self.other_posit - 1 if self.other_posit > 0 else False,
                    '应下': self.other_posit + 1 if self.other_posit < 5 else False,  # 以上只对主卦的idx成立
                }
                key, value = '爻序', value_dict[string]  # 不在这里return，是因为往下走可以保存

            elif string.endswith('变爻'):  # former_string_range=(变爻冲,变爻合);current_string_range=(世爻变爻,)
                if not 5 < self.Memory_idx <= 11:
                    return False
                string = string[:-2]  # 去掉“变爻”
                key, value = self.convert_string['六亲六神'][string]  # 取 动爻列 and 需要比对的信息
                n = self.GuaImageDict['爻序'].index(self.Memory_idx)
                # 变爻爻位 转 idx，find out the index of 'self.Memory_idx'
                idx = int(self.GuaImageDict['发动'][n])  # 变爻idx 转 动爻idx
                add_to_memory = False

            elif string in TIME_STRING:  # 有很多“逢冲”的字眼，须放在“冲合”前缀之前
                key, value = self.time_string_identify(string)
            elif '冲' in string or '合' in string:  # ”化出冲世/化出合应” 算在上述的 化 字段；缺点:可能误处理一些：包含”冲合“的字段
                # if string.startswith('化出'):  if not 5 < idx <= 11:  return False   idx = self.GuaImageDict['爻序'].index(idx + 6)  string = string[2:]
                if string[1:] in LIU_QIN:
                    key, value = f'{string[0]}六亲', string
                elif string[1:] in self.lines_animals:
                    # 冲二爻，同时也可以冲六爻，一个单元格填不下两个，所以现场计算
                    dic = {'冲': xiangchong, '合': xianghe}
                    yao_wei_zhi = ZHI.index(self.GuaImageDict['支'][self.lines_animals[string[1:]]])  # 爻位的字符串地支，转int
                    key, value = '支', ZHI[dic[string[0]](yao_wei_zhi)]  # 用int算相冲相合的int，转字符串地支
                elif string[1:] in '世|应':
                    key, value = f'冲合{string[1:]}', string
                else:
                    print(string, f'其中的【{string[1:]}】是不规范的字符！注：冲世|合世|冲应|合应')

            # 查询 长生十二长生状态
            elif string[2:] in LIFE_AND_DEATH_12_FORTUNE:
                if string[:2] in '父母|兄弟|子孙|妻财|官鬼|世爻|应爻':
                    key, value = f'{string[:2]}地', string
                elif string[:2] in '日为|月为':
                    key = string[0] + '地'
                    value = LIFE_AND_DEATH_12_FORTUNE[string[2:]]

            elif string in SHEN_SHA_STR:
                key, value = '支', self.search_shen_sha(string)

            elif string.startswith('在'):  # former_string_range=(入动墓,化墓,病地发动);current_string_range=(在子孙,在妻财,)
                string = string[1:]  # 去掉“在”
                key, value = self.search_kind(string), string
                idx = self.Memory_idx
                add_to_memory = False

            try:  # 因为不知道用户会输入什么奇怪的字符
                condition = self.GuaImageDict[key][idx] == value
            except KeyError:
                print('-'.join(self.cache.tmp), key, idx, string, '\n※    这个导致错误！')
            except TypeError:
                print('-'.join(self.cache.tmp), key, idx, '\n※    这里有一个类型错误！')
            except UnboundLocalError:
                print("※    未能识别的字符串:", string, "\n※    当前表格行:", idx, "\n※    完整句子:", self.cache.tmp)

        if add_to_memory:
            self.cache[idx][string] = condition  # 确认添加，则加入记忆

        return condition

    def get_objIdxList(self, obj, col, includeMonthDate):
        vocabulary = {
            '六爻':(0,'主卦'),'五爻':(1,'主卦'),
            '父母':('六亲','主卦|伏神'),
            '化出':('变爻',),'伏藏':('伏神',),'两现':(2,'length')

        }
        if includeMonthDate:
            li = self.GuaImageDict[col]
        else:
            li = self.GuaImageDict[col][:-2]            # 不包括 最后两项（日、月）

        # 父母兄弟子孙妻财官鬼

        return [i for i in range(6) if li[i] == obj]

    def idx_set(self, obj: str = None, col: str = None, include: bool = False, obj_list: list[str] = None) -> list[int]:
        if not col:
            col = self.search_kind(obj if obj else obj_list[0])
        if include:
            li = self.GuaImageDict[col]
        else:
            # 不包括 最后两项（日、月）
            li = self.GuaImageDict[col][:-2]
        if obj:
            return [i for i, item in enumerate(li) if obj == item]
        elif obj_list:
            return [i for i, item in enumerate(li) if item in obj_list]

    def 主卦六亲索引(self, 主卦六亲: str) -> list[int]:
        li = [i for i in range(6) if self.GuaImageDict['六亲'][i] == 主卦六亲]
        if li == []:
            li = self.伏藏索引(主卦六亲)
        return li

    def range_to_idx_lst(self, sup=18, low=12):
        idx_lst = []
        lst = self.GuaImageDict['爻序']  # 这是升序的数组，倒着数更快
        for idx in range(len(lst) - 1, -1, -1):
            if low <= lst[idx] < sup:
                idx_lst.append(idx)
            elif lst[idx] < low:
                break
        return idx_lst
        # 选中'爻序'在某个范围之内的所有idx，作为一个idx_set
        # return [i for i,num in enumerate(self.GuaImageDict['爻序']) if 12 <= lst[i] < 18]，上述函数的运行速度 比这句快一倍

    def 伏藏索引(self, 伏藏六亲: str) -> list[int]:
        # 所有伏藏位置的列为备选，用if筛选
        res = [i for i in self.暂存箱['伏神'] if self.GuaImageDict['六亲'][i] == 伏藏六亲]
        if res:
            self.暂存箱['飞神'] = [self.GuaImageDict['爻序'][res[0]] % 12]  # res不能是[]，索引失去意义
        return res

    def 变爻索引(self, 动爻六亲: str = None, 动爻索引: list[int] = None, 变爻六亲: str = None) -> list[int]:
        # 动爻六亲，求变爻
        # solution:所有变爻位置的列为备选，用if筛选
        yao_xu_lst = self.GuaImageDict['爻序']
        if 动爻六亲:
            return [yao_xu_lst.index(i + 6) for i in self.暂存箱['动爻'] if
                    self.GuaImageDict['六亲'][i] == 动爻六亲]
        elif 动爻索引:
            # 最好加一个限制，动爻索引列表 all in [0,1,2,3,4,5]  ，表格的规律是这样的
            return [yao_xu_lst.index(i + 6) for i in 动爻索引 if i <= 5 and self.trigger_li[i]]
        elif 变爻六亲:
            return [i for i in self.暂存箱['变爻'] if self.GuaImageDict['六亲'][i] == 动爻六亲]
        else:
            print('动爻六亲，动爻序号，变爻六亲 必须输入一个！')
            return []

    def isTargetYao(self,i,string):
        def func(i,c):
            return 1234
        if '|' in string:
            return any([s for s in string.split('|')])
        else:
            return func(i,c)



    def read_lines(self):
        pass


def isHaveMonthDate(dic) -> bool:
    return bool(dic) and all(n is not None for n in [dic['m'], dic['d'], dic['dg']])


'''排盘辅助函数'''


def get_time_info(attrDic) -> str:
    if all(n is not None for n in [attrDic.m, attrDic.dg, attrDic.d]):
        _1 = GAN[attrDic.Yg % 10] if attrDic.Yg is not None else ''
        _2 = _1 + ZHI[attrDic.Y % 12] + '年' if attrDic.Y is not None else ''
        _3 = ZHI[attrDic.m % 12] + '月'
        _4 = GAN[attrDic.dg % 10] + ZHI[attrDic.d % 12] + '日'
        _5 = ZHI[attrDic.H % 12] + '时' if attrDic.H is not None else ''

        ric_gan_gap = attrDic.d - attrDic.dg if attrDic.dg else attrDic.d - 10  # 癸亥日，0-0，变成首位的“戌亥”
        empty = f'（空亡:{KONG_WANG[(ric_gan_gap + 12) % 12 // 2]}）'

        return ' '.join([_2, _3, _4, _5]) + empty
    else:
        return '日月不明'


def h_calculate(gua_num: int, ri_gan: int):
    REMINDER_TO_WU_XING = [4, 3, 1, 3, 0, 0, 2, 4]
    REMAINDER_TO_SIX_MODE = [0, 1, 1, 2, 2, 3, 4, 5, 5, 0]
    return REMINDER_TO_WU_XING[gua_num // 8], REMAINDER_TO_SIX_MODE[ri_gan % 10]


def gua_info_list_calculate(gua_num: int, biangua_num: int, trigger_li: list[int]):
    def search_After():
        li = [None] * 6
        if biangua_num <= 63:  # 不是静卦的话
            Biangua_li = GUA_SHU[biangua_num]
            for i, n in enumerate(trigger_li):
                if n:
                    li[i] = Biangua_li[i]
        return li

    # 顺序是Main，After，Hide
    li = GUA_SHU[gua_num].copy()  # 直接赋值的话，li将会直接在gua_shu的内存上修改extend，影响下次赋值。
    li.extend(search_After())
    li.extend(HIDE_SHU[gua_num])  # GuaShu_li.extend(search_hide(self.gua_num))
    return li


def search_self(Guanum: int):
    '''
    给卦的序号，找出世应的idx
    :param Guanum:卦的序号
    :return: List[self_idx,other_idx]（5-0代表初爻到六爻）
    '''
    remain = Guanum % 8
    # idx_to_selfposit = [6, 1, 2, 3, 4, 5, 4, 3]原本是1-6代表初爻到六爻
    REMAINDER_SELF = [0, 5, 4, 3, 2, 1, 2, 3]  # 现在是5-0代表初爻到六爻，贴合gua_shu的习惯
    REMAINDER_OTHER = [3, 2, 1, 0, 5, 4, 5, 0]
    return [REMAINDER_SELF[remain], REMAINDER_OTHER[remain]]


def self_other_ps(self_posit, other_posit, skip=False):
    self_other_lst = [''] * 18
    self_other_lst[self_posit] = '世'
    self_other_lst[other_posit] = '应'
    if not skip:
        max_num = max(self_posit, other_posit)
        self_other_lst[max_num - 2], self_other_lst[max_num - 1] = '间', '间'
    return self_other_lst


def Sixmode_to_Animals(Sixmode: int) -> list[str]:
    li = [ANIMALS[(Sixmode + 10 - i) % 6] for i in range(6)]
    return li


def palace_12(WuXing):
    return [FAMILY[num] for num in LIU_QIN_NUM_60[WuXing]]


"""计算公式"""


def xiangchong(num):  # range:0-11
    num = num % 12
    return num - 6 if num >= 6 else num + 6


def xianghe(num):
    num = num % 12
    return 3 - num if num <= 3 else 15 - num


def 冲合值(地支: int, 活跃地支: int):
    相冲 = xiangchong(活跃地支)
    相合 = xianghe(活跃地支)
    相害 = xianghe(相冲)
    if 地支 == 活跃地支:
        return "值"
    elif 地支 == 相冲:
        return "冲"
    elif 地支 == 相合:
        return "合"
    elif 地支 == 相害:
        return "害"
    else:
        return ""


def 生扶克(地支: int, 活跃地支: int, mode=None):
    # 【注意】必须输入0-12，否则就会崩溃
    if 活跃地支 is None or 地支 is None:
        print(f'出错了！！活跃地支是：【{活跃地支}】', f'地支是：【{地支}】')
        return ''

    li = [[2, 5, 8, 11], [9, 10], [1, 13, 12, 0], [3, 4], [6, 7]]
    for i in range(5):
        if 地支 in li[i]:
            zhi_idx = i
        if 活跃地支 in li[i]:
            active_zhi_idx = i

    res_li = ["扶", '生', '克', '', '']
    if mode == '变爻':
        text = "退" if 地支 > 活跃地支 else "进" if 地支 < 活跃地支 else "吟"
        res_li[0] = text
    elif mode == '月破' and zhi_idx == 0 and xiangchong(地支) == 活跃地支:
        return ''
    try:
        res = res_li[(zhi_idx - active_zhi_idx + 5) % 5]
        return res

    except UnboundLocalError:
        print('地支，活跃地支：', 地支, 活跃地支)
        return ''


def 刑(地支: int, 活跃地支: int):
    li_item = [[3, 6, 9], [5, 7, 10, 12, 0], [1, 4, 13], [2, 8, 11]]
    if 地支 == 活跃地支 and 地支 not in [5, 7, 10, 12, 0]:
        return ''
    for li in li_item:
        if 地支 in li:  # 如果“地支”在某一组中，再判断“活跃地支”在不在。不在同一组直接break，减少不必要的判断次数。
            if 活跃地支 in li:
                return '刑'
            else:
                return ''


def 计数刑(地支: int, 活跃地支列表: list[int]):
    num = sum(1 for 活跃地支 in 活跃地支列表 if 刑(地支, 活跃地支))
    return num


if __name__ == "__main__":
    msg_dict = {'title': '', 'my': None, 'coinNum': [1,2,1,2,1,2], 'gua': 25, 'biangua': 26}
    time_dict = {'Y': None, 'Yg': None, 'm': None, 'd': None, 'dg': None, 'H': None}
    m = LiuyaoPlace(time_dict, msg_dict)
    m.place(output=True)
    print(m.time.Yg)
